﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Events;

namespace Elvex
{
    public class StateMachine<TState>
    {
        public class Transition
        {
            public Transition(TState source, TState destination)
            {
                Source = source;
                Destination = destination;
            }

            public TState Source { get; }
            public TState Destination { get; }
        }

        public class Trigger
        {
            private Dictionary<TState, Action> Actions { get; } = new Dictionary<TState, Action>();

            public void Fire(TState state)
            {
                if(Actions.TryGetValue(state, out var action))
                {
                    action.Invoke();
                }
            }

            public void On(TState state, Action callback)
            {
                Actions.Add(state, callback);
            }
        }

        public class Trigger<TArg>
        {
            private Dictionary<TState, Action<TArg>> Actions { get; } = new Dictionary<TState, Action<TArg>>();
            public void Fire(TState state, TArg arg)
            {
                if (Actions.TryGetValue(state, out var action))
                {
                    action.Invoke(arg);
                }
            }

            public void On(TState state, Action<TArg> callback)
            {
                Actions.Add(state, callback);
            }
        }

        public class StateConfigure
        {
            public TState State { get; private set; }
            public StateMachine<TState> Machine { get; private set; }

            public Action EntryAction { get; private set; }

            public Action ExitAction { get; private set; }

            public StateConfigure(TState state, StateMachine<TState> machine)
            {
                State = state;
                Machine = machine;
            }

            public StateConfigure OnEntry(Action callback)
            {
                EntryAction = callback;
                return this;
            }

            public StateConfigure OnExit(Action callback)
            {
                ExitAction = callback;
                return this;
            }

            public StateConfigure Permit(Trigger trigger, TState state)
            {
                trigger.On(State, () =>
                {
                    Transition transition = new Transition(State, state);
                    Machine.ChangeState(transition);
                });
                return this;
            }

            public StateConfigure PermitIf(Trigger trigger, TState state, Func<bool> check)
            {
                trigger.On(State, () =>
                {
                    if(check())
                    {
                        Transition transition = new Transition(State, state);
                        Machine.ChangeState(transition);
                    }
                });
                return this;
            }

            public StateConfigure Permit(Trigger trigger, Func<TState> handler)
            {
                trigger.On(State, () =>
                {
                    Transition transition = new Transition(State, handler.Invoke());
                    Machine.ChangeState(transition);
                });
                return this;
            }

            public StateConfigure Permit<TArg>(Trigger<TArg> trigger, Func<TArg, TState> handler)
            {
                trigger.On(State, (arg) =>
                {
                    Transition transition = new Transition(State, handler.Invoke(arg));
                    Machine.ChangeState(transition);
                });
                return this;
            }
        }

        public TState CurrentState { get; private set; } // 当前状态

        private Dictionary<TState, StateConfigure> m_stateConfigures = new Dictionary<TState, StateConfigure>(); // 状态配置

        public StateMachine(TState state)
        {
            CurrentState = state;
        }

        public StateConfigure Configure(TState state)
        { 
            if(m_stateConfigures.TryGetValue(state, out var cfg))
            {
                return cfg;
            }
            else
            {
                m_stateConfigures.Add(state, new StateConfigure(state, this));
                return m_stateConfigures[state];
            }
        }

        public void Fire(Trigger trigger)
        {
            trigger.Fire(CurrentState);
        }

        public void Fire<T>(Trigger<T> trigger, T arg)
        {
            trigger.Fire(CurrentState, arg);
        }

        private void ChangeState(in Transition transition)
        {
            if (m_stateConfigures.TryGetValue(transition.Source, out var cfg))
            {
                cfg.ExitAction?.Invoke();
            }

            CurrentState = transition.Destination;

            if (m_stateConfigures.TryGetValue(transition.Destination, out var newcfg))
            {
                newcfg.EntryAction?.Invoke();
            }
        }
    }
}

